import json
import urllib.parse

from adminlteui.templatetags.adminlte_list import get_filter_id
from django.contrib.admin.templatetags.base import InclusionAdminNode
from django.contrib.admin.views.main import ChangeList, SEARCH_VAR
from django.template import Library
from django.template.loader import get_template
from django.utils.encoding import force_text
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.translation import gettext as _
from mptt.admin import TreeRelatedFieldListFilter
from mptt.models import MPTTModel

from chatbot.const import const

register = Library()


@register.simple_tag
def has_tree_filter(specs):
    for spec in specs:
        if is_tree_filter(spec):
            return True
    return False


@register.simple_tag
def is_tree_filter(spec):
    return isinstance(spec, TreeRelatedFieldListFilter)


def search_form(cl):
    """
    Display a search form for searching the list.
    """
    return {
        'cl': cl,
        'show_result_count': cl.result_count != cl.full_result_count,
        'search_var': SEARCH_VAR
    }


@register.tag(name='tree_search_form')
def tree_search_form_tag(parser, token):
    return InclusionAdminNode(parser, token, func=search_form, template_name='../chatbot/search_form.html',
                              takes_context=False)


@register.simple_tag
def admin_list_tree_filter(cl: ChangeList, spec):
    choices = list(spec.choices(cl))
    field_key = get_filter_id(spec)
    matched_key = field_key
    tpl = get_template(spec.template)
    if is_tree_filter(spec):
        return tpl.render({})
    for choice in choices:
        query_string = choice['query_string'][1:]
        query_parts = urllib.parse.parse_qs(query_string)

        value = ''
        matches = {}
        for key in query_parts.keys():
            if key == field_key:
                value = query_parts[key][0]
                matched_key = key
            elif key.startswith(
                    field_key + '__') or '__' + field_key + '__' in key:
                value = query_parts[key][0]
                matched_key = key

            if value:
                matches[matched_key] = value

        # Iterate matches, use first as actual values, additional for hidden
        i = 0
        for key, value in matches.items():
            if i == 0:
                choice['name'] = key
                choice['value'] = value
            # else:
            #     choice['additional'] = '%s=%s' % (key, value)
            i += 1

    return tpl.render({
        'field_name': field_key,
        'title': spec.title,
        'choices': choices,
        'spec': spec,
    })


@register.simple_tag
def render_tree_filter(cl: ChangeList):
    filter_spec = list(filter(is_tree_filter, cl.filter_specs))
    tpl = get_template('chatbot/tree_filter.html')

    if not filter_spec:
        return tpl.render({
            'core': {}
        })
    tree_spec = filter_spec[0]
    tree_model: MPTTModel = tree_spec.other_model
    choices = list(tree_spec.choices(cl))
    qs = tree_model.objects.order_by('tree_id', 'parent', 'lft')

    selected = (int(tree_spec.lookup_val),) if tree_spec.lookup_val else (0,)

    query_strings = {}

    for idx, choice in enumerate(tree_spec.lookup_choices):
        choice_id = choice[0]
        query_string = cl.get_query_string({
            tree_spec.changed_lookup_kwarg: choice_id,
        }, [tree_spec.lookup_kwarg_isnull])
        query_strings[choice_id] = query_string

    for choice in choices:
        query_string = choice['query_string']
        if choice['display'] == _('All'):
            query_strings[0] = query_string
            break

    formatter = TreeFormatter('id', selected, (), query_strings)
    tree_data = formatter.render(qs)

    json_data = json.dumps({'data': tree_data})

    return tpl.render({
        'core': mark_safe(json_data.replace('</script', '<\/script'))
    })


class TreeFormatter(object):
    ID_TEMPLATE = 'tree_%s_%s'

    def __init__(self, attr_name, selected, disabled, choices):
        self.attr_name = attr_name
        self.selected = selected
        self.disabled = disabled
        self.choices = choices
        self.top_parent = self.ID_TEMPLATE % (self.attr_name, 0)

    def render(self, queryset):
        """
        Render method of tree data for `jstree`.
        NOTE: `queryset` is a `tree.TreeQueryset` object.
        To avoid expensive database lookups, the parent pk
        is accessible as `node.node._parent_pk`.
        """

        result = [{
            'id': self.top_parent,
            'parent': '#',
            'text': escape(force_text(const.FILTER_TYPE_ALL)),
            'data': {
                'query_string': self.choices.get(0, '?')
            },
            'state': {
                'selected': True if 0 in self.selected else False,
                'disabled': False
            }
        }]
        for node in queryset:
            node_id = self.ID_TEMPLATE % (self.attr_name, node.pk)
            parent = self.top_parent
            if node.parent_id:
                parent = self.ID_TEMPLATE % (self.attr_name, node.parent_id)

            result.append({
                'id': node_id,
                'parent': parent,
                'text': escape(force_text(node.name)),
                'data': {
                    'query_string': self.choices[node.pk]
                },
                'state': {
                    'selected': True if node.pk in self.selected else False,
                    'disabled': True if node.pk in self.disabled else False
                }
            })
        return result
